﻿/*
 * MIT License
 *
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#include <iostream>
#include "Util/logger.h"
using namespace std;
using namespace toolkit;

class TestLog
{
public:
    template<typename T>
    TestLog(const T &t){
        _ss << t;
    };
    ~TestLog(){};

    //通过此友元方法，可以打印自定义数据类型
    friend ostream& operator<<(ostream& out,const TestLog& obj){
        return out << obj._ss.str();
    }
private:
    stringstream _ss;
/*
<sstream> 定义了三个类：istringstream、ostringstream 和 stringstream，分别用来进行流的输入、输出和输入输出操作。
本文以 stringstream 为主，介绍流的输入和输出操作。

<sstream> 主要用来进行数据类型转换，由于 <sstream> 使用 string 对象来代替字符数组（snprintf方式），就避免缓冲区溢出的危险；
而且，因为传入参数和目标对象的类型会被自动推导出来，
所以不存在错误的格式化符的问题。简单说，相比c库的数据类型转换而言，<sstream> 更加安全、自动和直接
eg1:
 stringstream sstream;    
 string strResult;    
 int nValue = 1000;     

 // 将int类型的值放入输入流中    
 sstream << nValue;
 // 从sstream中抽取前面插入的int类型的值，赋给string类型 
 sstream >> strResult;     
 cout << "[cout]strResult is: " << strResult << endl;    
 printf("[printf]strResult is: %s\n", strResult.c_str());

 eg2:
 stringstream sstream;
 // 将多个字符串放入sstream中
 sstream << "first" << " " << "string,";
 sstream << " second string";
 count << "strResult is:" << sstream.str() << endl;
 // 清空sstream
 sstream.str("");

 可以使用 str() 方法，将 stringstream 类型转换为 string 类型；
 可以将多个字符串放入 stringstream 中，实现字符串的拼接目的；
 如果想清空 stringstream，必须使用 sstream.str(""); 方式；clear() 方法适用于进行多次数据类型转换的场景

 stringstream sstream;    
 int first, second;     
 // 插入字符串    
 sstream << "456";    
 // 转换为int类型 
 sstream >> first;    
 cout << first << endl;     

 // 在进行多次类型转换前，必须先运行clear()
 sstream.clear();     
 // 插入bool值    
 sstream << true;    
 // 转换为int类型 
 sstream >> second;    
 cout << second << endl;
*/
};

int main() {
    //初始化日志系统
    Logger::Instance().add(std::make_shared<ConsoleChannel> ());
    Logger::Instance().add(std::make_shared<FileChannel>());
    Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
/*
模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr ->shared_ptr 需要维护引用计数的信息
1、std::shared_ptr<Widget> spw(new Widget);
很明显这段代码需要分配内存，但是它实际上要分配两次。每个std::shared_ptr都指向一个控制块，
控制块包含被指向对象的引用计数以及其他东西。这个控制块的内存是在std::shared_ptr的构造函数中分配的。
因此直接使用new，需要一块内存分配给Widget，还要一块内存分配给控制块。
2、auto spw = std::make_shared<Widget>();
一次分配就足够了。这是因为std::make_shared申请一个单独的内存块来同时存放Widget对象和控制块。
这个优化减少了程序的静态大小，因为代码只包含一次内存分配的调用，并且这会加快代码的执行速度，
因为内存只分配了一次。另外，使用std::make_shared消除了一些控制块需要记录的信息，这样潜在地减少了程序的总内存占用。
*/

    //ostream支持的数据类型都支持,可以通过友元的方式打印自定义类型数据
    TraceL << "object int"<< TestLog((int)1)  << endl;
    DebugL << "object short:"<<TestLog((short)2)  << endl;
    InfoL << "object float:" << TestLog((float)3.12345678)  << endl;
    WarnL << "object double:" << TestLog((double)4.12345678901234567)  << endl;
    ErrorL << "object void *:" << TestLog((void *)0x12345678) << endl;
    ErrorL << "object string:" << TestLog("test string") << endl;


    //这是ostream原生支持的数据类型
    TraceL << "int"<< (int)1  << endl;
    DebugL << "short:"<< (short)2  << endl;
    InfoL << "float:" << (float)3.12345678  << endl;
    WarnL << "double:" << (double)4.12345678901234567  << endl;
    ErrorL << "void *:" << (void *)0x12345678 << endl;
    //根据RAII的原理，此处不需要输入 endl，也会在被函数栈pop时打印log
    ErrorL << "without endl!";

/*
 RAII是Resource Acquisition Is Initialization的简称，资源获取即初始化；
 是C++语言的一种管理资源、避免泄漏的惯用法。
 利用的就是C++构造的对象最终会被销毁的原则。
 RAII的做法是使用一个对象，在其构造时获取对应的资源，在对象生命期内控制对资源的访问，
 使之始终保持有效，最后在对象析构的时候，释放构造时获取的资源。

 RAII的本质内容是用对象代表资源，把管理资源的任务转化为管理对象的任务，
 将资源的获取和释放与对象的构造和析构对应起来，从而确保在对象的生存期内资源始终有效，
 对象销毁时资源一定会被释放。说白了，就是拥有了对象，就拥有了资源，对象在，资源则在。
 所以，RAII机制是进行资源管理的有力武器，C++程序员依靠RAII写出的代码不仅简洁优雅，
 而且做到了异常安全。在以后的编程实际中，可以使用RAII机制，让自己的代码更漂亮

 当我们在一个函数内部使用局部变量，当退出了这个局部变量的作用域时，这个变量也就别销毁了；
 当这个变量是类对象时，这个时候，就会自动调用这个类的析构函数，而这一切都是自动发生的，不要程序员显示的去调用完成
*/
    return 0;
}
